﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SuperKit
{
    class FileCollector : CommandExecutor {
        public DirectoryInfo DirInfo { get; private set; }
        public string Ext { get; }
        public string[] KeyWords { get; }
        public float MinSize { get; }
        public float MaxSize { get; }
        public string TargetPath { get; private set; }
        public bool IsDirectory { get; }


        public FileCollector(ArgReader arg, IExecutorOwner form, string fpath) : base(arg, form, fpath) {
            Ext = arg.GetParameter("-type");
            var keyword = arg.GetParameter("-keyword");
            KeyWords = Kit.IsEmpty(keyword) ? null : keyword.Split('|');
            MinSize = arg.GetFloat("-minsize");
            MaxSize = arg.GetFloat("-maxsize");
            IsDirectory = arg.IsContain("-directory");
        }



        public override void Execute() {
            base.Execute();

            DirInfo = new DirectoryInfo(FilePath);

            if (IsDirectory) {
                ExecuteDirs();
            } else {
                ExecuteFiles();
            }
            

            End();
        }

        private void ExecuteDirs() {
            List<DirectoryInfo> dirs = new List<DirectoryInfo>();
            GetAllDirectories(DirInfo, dirs);
            dirs = FilterKeywords(dirs);
            MoveDir(dirs);
        }


        private void ExecuteFiles() {
            
            List<FileInfo> files = new List<FileInfo>();
            GetAllFiles(DirInfo, files, GetExts(Ext));
            files = FilterKeywords(files);
            List<FileInfo> filtered = new List<FileInfo>(files.Count);
            foreach (var i in files) {
                float mb = (float)i.Length / (float)Kit.MB;
                if (MinSize > Kit.TINY && mb < MinSize) {
                    Flush("Passed by minsize({0:N3}M):{1:N3}M", MinSize, mb);
                    continue;
                }
                if (MaxSize > Kit.TINY && mb > MaxSize) {
                    Flush("Passed by maxsize({0:N3}M):{1:N3}M", MinSize, mb);
                    continue;
                }
                filtered.Add(i);
            }

            Move(filtered);
        }

        private List<FileInfo> FilterKeywords(List<FileInfo> files) {
            if (KeyWords == null) {
                return files;
            }
            List<FileInfo> newList = new List<FileInfo>(files.Count);
            foreach (var i in files) {
                if (IsContain(KeyWords, i.Name)) {
                    newList.Add(i);
                }
            }
            return newList;
        }

        private List<DirectoryInfo> FilterKeywords(List<DirectoryInfo> files) {
            if (KeyWords == null) {
                return files;
            }
            List<DirectoryInfo> newList = new List<DirectoryInfo>(files.Count);
            foreach (var i in files) {
                if (IsContain(KeyWords, i.Name)) {
                    newList.Add(i);
                }
            }
            return newList;
        }

        private bool IsContain(string[] list, string name) {
            foreach (var i in list) {
                if (name.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1) {
                    return true;
                }
            }
            return false;
        }

        private void Move(List<FileInfo> list) {
            if (Kit.Len(list) == 0) {
                Flush("没有任何文件符合条件。");
                return;
            }

            TargetPath = CreateTargetDir();

            PrintResult(list);

            foreach (var i in list) {
                var newPath = CalculateNewPath(DirInfo.FullName, TargetPath, i.FullName);
                MoveFile(i.FullName, newPath);                
            }
        }


        private void MoveDir(List<DirectoryInfo> list) {
            TargetPath = CreateTargetDir();

            PrintResult(list);

            foreach (var i in list) {
                var newPath = CalculateNewPath(DirInfo.FullName, TargetPath, i.FullName);

                // 如果此目录不存在了，应该是父目录被移走了，直接跳过.
                if (Directory.Exists(i.FullName) == false) {
                    Flush("目录已经不存在：{0}", i.FullName);
                    continue;
                }

                if (Directory.Exists(newPath)) {
                    Flush("目标目录已经存在：{0}", newPath);
                    continue;
                }

                try {
                    DirectoryInfo destInfo = new DirectoryInfo(newPath);
                    if (destInfo.Parent.Exists == false) {
                        destInfo.Parent.Create();
                    }
                    Directory.Move(i.FullName, newPath);
                }catch (Exception e) {
                    Flush(e.ToString());
                }

            }
        }


        private new string CreateTargetDir() {
            var targetDir = Kit.IsEmpty(TargetDir) ? "(_Collect)" : TargetDir;
            var targetPath = Path.Combine(DirInfo.FullName, targetDir);
            if (Directory.Exists(targetPath) == false) {
                Directory.CreateDirectory(targetPath);
            }
            Flush("目标收集目录：{0}", targetPath);
            return targetPath;
        }

        private void PrintResult(List<FileInfo> list) {
            double size = 0;
            foreach (var i in list) {
                size += i.Length;                
                SetStatus("找到：{0}", i.FullName);
                var newPath = CalculateNewPath(DirInfo.FullName, TargetPath, i.FullName);

            }
            Flush("总计({0})个文件，总计({1:N3})GB", list.Count, size / Kit.GB);
        }

        private void PrintResult(List<DirectoryInfo> list) {
            foreach (var i in list) {
                var newPath = CalculateNewPath(DirInfo.FullName, TargetPath, i.FullName);
                Flush($"{i.FullName}=>{newPath}", newPath);
            }
            Flush("总计({0})个目录", list.Count);
        }

        public static string CalculateNewPath(string parent, string targetPath, string fullName) {
            var dirPath = Path.GetFullPath(parent);
            var leftPath = fullName[(dirPath.Length + 1)..]; // 这个加1是因为DirPath最后没有那个斜杠，而FullPath有。
            // 获得第一个目录，这个需要做为分类存在
            int firstDirIdx = leftPath.IndexOf("\\");
            string firstDir = string.Empty;
            if (firstDirIdx != -1) {
                firstDir = leftPath[..firstDirIdx];
                leftPath = leftPath[(firstDirIdx + 1)..];
            }
            var namePath = leftPath.Replace("\\", "_");
            if (Kit.IsEmpty(firstDir)) {
                return Path.Combine(targetPath, namePath);
            } else {
                return Path.Combine(targetPath, firstDir, namePath);
            }
        }

        public string[] GetExts(string ext) {
            if (Kit.IsEmpty(ext)) {
                return Kit.ALL_EXTS;
            }
            var list = ext.Split(Kit.ELEMENT_SPLIT);
            var adjust = new string[list.Length];
            for (int i = 0; i < list.Length; i++) {
                adjust[i] = AdjustExt(list[i]);
            }
            return adjust;
        }

        private static string AdjustExt(string v) {
            // 不需要以*.开头，但是*.mpg也是一个正确的写法，所以将*.变成.。
            v = v.Replace("*.", ".");
            // 如果没有以.开头，表示只写了后缀名，需要加上.以匹配比较，因为Kit比较后缀名时，输入的队列是.mp4,.mp3这样的以.为先的。
            if (v.IndexOf('.') == -1) {
                v = "." + v;
            }
            return v;
        }
    }
}
